#include "MsgRouter.h"
#include "log.h"
#include "util.h"
#include "Game.h"
#include "def.h"
#include "context.h"

#include <csignal>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

using socket_t = int;
#ifndef INVALID_SOCKET
#define INVALID_SOCKET (-1)
#endif


inline bool get_ip_and_port(const struct sockaddr_storage &addr,
                            socklen_t addr_len, std::string &ip, int &port) {
  if (addr.ss_family == AF_INET) {
    port = ntohs(reinterpret_cast<const struct sockaddr_in *>(&addr)->sin_port);
  } else if (addr.ss_family == AF_INET6) {
    port =
        ntohs(reinterpret_cast<const struct sockaddr_in6 *>(&addr)->sin6_port);
  } else {
    return false;
  }

  std::array<char, NI_MAXHOST> ipstr{};
  if (getnameinfo(reinterpret_cast<const struct sockaddr *>(&addr), addr_len,
                  ipstr.data(), static_cast<socklen_t>(ipstr.size()), nullptr,
                  0, NI_NUMERICHOST)) {
    return false;
  }

  ip = ipstr.data();
  return true;
}

inline void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) {
  struct sockaddr_storage addr;
  socklen_t addr_len = sizeof(addr);

  if (!getpeername(sock, reinterpret_cast<struct sockaddr *>(&addr),
                   &addr_len)) {
    if (addr.ss_family == AF_UNIX) {
      struct ucred ucred;
      socklen_t len = sizeof(ucred);
      if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == 0) {
        port = ucred.pid;
      }
      return;
    }
    get_ip_and_port(addr, addr_len, ip, port);
  }
}

inline bool AssertMsg(bool state, const char* msg)
{
	if (!state)
	{
		log_error("assert failed,%s", msg);
	}
	return state;
}

#ifndef Assert
# define Assert(expr) if (!AssertMsg(expr, __STRING(expr))) return  
#endif

static std::string ToUpStr(std::string str)
{
    int distance = 'A' - 'a';
    for (auto & ch : str)
    {
        if (ch >= 'a' && ch <= 'z')
        {
            ch += distance;
        }
    }
    return str;
}
static std::string ToLowStr(std::string str)
{
    int distance = 'a' - 'A';
    for (auto & ch : str)
    {
        if (ch >= 'A' && ch <= 'Z')
        {
            ch += distance;
        }
    }
    return str;
}

std::map<std::string, std::string> ParseUrlParams(std::string params_str)
{
    std::map<std::string, std::string> params;

    char* s = (char*)params_str.c_str();
    char* e = s + params_str.size();
    char* p = s;
    char* key_s = s;
    char* key_e = s;
    char* val_s = s;
    char* val_e = s;
    char word = '&';
    char sep = '=';
    while (p <= e)
    {
        if (p == e)
        {
            if (key_s != val_s)
            {
                val_e = p;
                params[std::string(key_s, key_e - key_s)] = std::string(val_s, val_e - val_s);
            }

            break;
        }
        else if (*p == word)
        {
            if (key_s != val_s)
            {
                val_e = p;
                params[std::string(key_s, key_e - key_s)] = std::string(val_s, val_e - val_s);
            }

            // 重置区间
            key_s = key_e = val_s = val_e = p + 1;
        }
        else if (*p == sep)
        {
            key_e = p;
            val_s = p + 1;
        }
        else
        {
            // 
        }
        p++;
    }

    return params;
}

MainHandler::MainHandler() : HiveNS::Handler() {

}
MainHandler::~MainHandler(){

}

// from Handler
void MainHandler::onReceiveHttp(HiveNS::Http* pHttp){
    HttpData data;
    data.request_id = pHttp->getHandle();
    data.method = std::string(http_method_str((http_method)pHttp->getParser()->method));
    data.target = std::string(pHttp->getUrl(), pHttp->getUrlLength());
    data.body = std::string(pHttp->getBody(), pHttp->getBodyLength());
    get_remote_ip_and_port(pHttp->getSocketFD(), data.ip, data.port);
    Ctx.msg_router.ReceiveHttpData(data);
}
void MainHandler::onCurlResponse(HiveNS::Buffer* pBuffer, uint32 nodeID, uint32 callbackID, bool isRequestOK, long code){
    CurlData data;
    data.callback_id = callbackID;
    data.body = std::string(pBuffer->data(), pBuffer->size());
    data.isOK = isRequestOK;
    data.code = code;
    Ctx.msg_router.ReceiveCurlData(data);
}
void MainHandler::onCloseHttp(HiveNS::Http* pHttp){

}
void MainHandler::onCloseListener(HiveNS::Listener* pListener){

}


int MsgRouter::OnProcessPacket(MsgData&& msg_data)
{
	int cmd = msg_data.cmd;
	log_debug("cmd=0x%x", cmd);
	try
	{
        auto it = m_msg_handler.find(cmd);
        if (it == m_msg_handler.end())
        {
		    log_info("cmd not found:%d", cmd);
            return -1;
        }
        it->second(std::move(msg_data));
	}
	catch(const std::exception& e)
	{
		log_error("cmd=0x%x,error:%s", cmd, e.what());
		return 0;
	}
	catch(...)
	{
		log_error("cmd=0x%x, unknown exception", cmd);
		return 0;
	}
	
	return 0;
}

int MsgRouter::OnProcessTimer(int Timerid)
{
	try
	{
        auto it = m_timer_handler.find(Timerid);
        if (it == m_timer_handler.end())
        {
		    log_info("timer not found:%d", Timerid);
            return -1;
        }
        it->second(Timerid);
	}
	catch(const std::exception& e)
	{
		log_info("catch exception:%s", e.what());
	}
	catch(...)
	{
		log_info("catch unknown exception");
	}
	return 0;
}

void MsgRouter::EventHandle()
{
    std::int32_t found = 0;
    ssize_t count_custom = 0;
    while (m_eventThreadRunState)
    {
        m_pSem.wait();
		count_custom = 0;
        do
        {
            if (found) 
            {
                count_custom += found;
                if (count_custom >> 10)
                {
                    m_pSem.signalCustom(count_custom);
                    log_debug("count_custom:%lu, m_msgqueue:%lu,m_timerqueue:%lu,m_rcvqueue:%lu,m_curlqueue:%lu", count_custom, m_msgqueue.size_approx(), m_timerqueue.size_approx(),m_rcvqueue.size_approx(),m_curlqueue.size_approx());
                    count_custom = 0;
                }
                found = 0;
            }

            {
                MsgData data;
                if (m_msgqueue.try_dequeue(data))
                {
                    OnProcessPacket(std::move(data));
                    found++;
                }
            }

            {
                int timer_id = 0;
                if (m_timerqueue.try_dequeue(timer_id))
                {
                    OnProcessTimer(timer_id);
                    found++;
                }
            }

            {
                HttpData data;
                if (m_rcvqueue.try_dequeue(data))
                {
                    OnGetMessage(std::move(data));
                    found++;
                }
            }

            {
                CurlData data;
                if (m_curlqueue.try_dequeue(data))
                {
                    OnCurlResponse(data.callback_id, data.body, data.isOK, data.code);
                    found++;
                }
            }
        } while (found);
        m_pSem.signalCustom(count_custom);
        // log_debug("count_custom, m_msgqueue:%d,m_timerqueue:%d,m_rcvqueue:%d,m_curlqueue:%d", count_custom, m_msgqueue.size_approx(), m_timerqueue.size_approx(),m_rcvqueue.size_approx(),m_curlqueue.size_approx());
    }
}

void MsgRouter::ReceiveMsgData(MsgData&& data)
{
    m_msgqueue.enqueue(std::move(data));
    m_pSem.signal();
}
void MsgRouter::ReceiveTimerData(int timer_id)
{
    m_timerqueue.enqueue(timer_id);
    m_pSem.signal();
}
void MsgRouter::ReceiveHttpData(HttpData& data){
    m_rcvqueue.enqueue(data);
    m_pSem.signal();
}
void MsgRouter::ReceiveCurlData(CurlData& data){
    m_curlqueue.enqueue(data);
    m_pSem.signal();
}

bool MsgRouter::httpServerBack(uint32 request_id, int status_code,
    const std::map<std::string, std::string>& header_map, const std::string& body){
#ifdef TRACE_MODE
    log_error("request_id:%u,us:%lld", request_id, CTools::GetUnixTimeMs());
#endif
    HiveNS::MainWorker::getInstance()->sendBackToRequest(request_id, status_code, body, header_map);
#ifdef TRACE_MODE
    log_error("request_id:%u,us:%lld", request_id, CTools::GetUnixTimeMs());
#endif
    return true;
}

void MsgRouter::OnGetMessage(HttpData&& data){
    log_debug("request_id=%d method=%s target=%s body=%s", data.request_id, data.method.c_str(), data.target.c_str(), data.body.c_str());

    std::string type = ToUpStr(data.method);

    size_t index = data.target.find_first_of('?');
    if (index == std::string::npos)
    {
        data.fullpath = data.target;
    }
    else 
    {
        data.fullpath = data.target.substr(0, index);
        data.params = ParseUrlParams(data.target.substr(index + 1));
    }

    // 使用正则表达式, 传参使用vector<string>表示路径,方便查询
    try
    {
        CTools::split_str(data.fullpath.c_str(), "/", data.sub_paths);
        for (auto& item : m_http_svr_handle[type])
        {
            log_debug("path:%s,match:%s", data.fullpath.c_str(), item.first.c_str());
            if ((data.fullpath.size() >= item.first.size()) && (0 == strncmp(item.first.c_str(), data.fullpath.c_str(), item.first.size())))
            {
                item.second(std::move(data));
                break;
            }
        }
    }
    catch(const std::exception& e)
    {
        log_debug("catch exception:%s", e.what());
    }
    catch(...)
    {
        log_debug("catch unknown exception");
    }
}
// curl接收到返回结果
void MsgRouter::OnCurlResponse(uint32 callback_id, std::string& body, bool isOK, long code){
    log_debug("OnCurlResponse callback_id=%u isOK=%d code=%ld body=%s", callback_id, isOK, code, body.c_str());
    auto it = m_curl_rsp_cb.find(callback_id);
    if (it == m_curl_rsp_cb.end())
    {
        return;
    }
    try
    {
        it->second(body, isOK && (code == -1 || code == 200));
    }
    catch(const std::exception& e)
    {
        log_debug("catch exception:%s", e.what());
    }
    catch(...)
    {
        log_debug("catch unknown exception");
    }
    m_curl_rsp_cb.erase(it);
}

bool MsgRouter::sendHttpPOST(const std::string& url, const std::string& body, const std::map<std::string, std::string>& headers, uint32& callbackID)
{
    try
    {
        HiveNS::MainWorker::getInstance()->sendHttpPOST(url, body, headers, callbackID);
        return true;
    }
    catch(const std::exception& e)
    {
        log_debug("catch exception:%s", e.what());
    }
    catch(...)
    {
        log_debug("catch unknown exception");
    }
    return false;
}

bool MsgRouter::sendHttpGET(const std::string& url, const std::string& body, const std::map<std::string, std::string>& headers, uint32& callbackID)
{
    try
    {
        HiveNS::MainWorker::getInstance()->sendHttpGET(url, body, headers, callbackID);
        return true;
    }
    catch(const std::exception& e)
    {
        log_debug("catch exception:%s", e.what());
    }
    catch(...)
    {
        log_debug("catch unknown exception");
    }
    return false;
}

void MsgRouter::BindHttpHandle(std::string method, std::string target, std::function<void(HttpData&& data)> handle)
{
    m_http_svr_handle[ToUpStr(method)][target] = handle;
}

void MsgRouter::AddCurlEvent(std::int32_t event_id, std::function<void(std::string&, bool)> event_handle)
{
    m_curl_rsp_cb[event_id] = event_handle;
}

MsgRouter::MsgRouter()
{
}
MsgRouter::~MsgRouter()
{
    Release();
}

bool MsgRouter::Init(int port)
{
    m_port = port;

    log_debug("InitGame called");
    // 创建httpserver线程
    m_pReceiveLock = new HiveNS::Sync();
    MainHandler* pMainHandler = new MainHandler();
    pMainHandler->retain();
    log_debug("InitGame MainHandler create");
    HiveNS::MainWorker::createInstance();
    HiveNS::MainWorker::getInstance()->setHandler(pMainHandler);
    // HiveNS::MainWorker::getInstance()->setListenerAddress("0.0.0.0", m_port);
    log_debug("InitGame MainWorker startThread create");
    HiveNS::MainWorker::getInstance()->startThread();

    m_eventThreadRunState = true;
    m_pEventHandleThread.reset(new std::thread(std::bind(&MsgRouter::EventHandle, this)));

    return true;
}

void MsgRouter::Reload()
{
}

void MsgRouter::Release()
{
} 








